﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityMultiPlayerGameServer.Clients;
using UnityMultiPlayerGameServer.Manager;
using GameProtocol;

namespace UnityMultiPlayerGameServer.Data
{
    internal class Room
    {
        private RoomInfo roomInfo;
        /// <summary>
        /// 存放当前房间玩家客户端
        /// string : accountid
        /// </summary>
        private Dictionary<string, Client> clientDict = new Dictionary<string, Client>();
        /// <summary>
        /// 存放玩家ID和玩家信息，用于返回客户端玩家信息
        /// </summary>
        //private Dictionary<string, UserInfo> userInfoDict = new Dictionary<string, UserInfo>();
        /// <summary>
        /// 房间房主
        /// </summary>
        private Client roomOwner;

        /// <summary>
        /// 标识房间是否已经开始游戏
        /// </summary>
        private bool isUsing = false;

        /// <summary>
        /// 当前房间准备就绪的人数
        /// </summary>
        private int preparedUser = 0;

        #region 属性
        internal Client RoomOwner { get => roomOwner; }
        internal RoomInfo RoomInfo { get => roomInfo; }
        // 标识房间是否已经开始游戏
        public bool IsUsing { get => isUsing; set => isUsing = value; }
        //当前房间剩余位置数
        public int RoomRemain { get => (roomInfo.RoomCapacity - roomInfo.UserCount); }
        //当前房间准备完毕的人数
        public int PreparedUser { get => preparedUser; set => preparedUser = value; }
        //当前房间是否所有人都准备游戏
        public bool isAllPlayerPrepared { get => preparedUser == roomInfo.UserCount; }

        #endregion

        public Room(string roomName, Client roomOwner, int roomCapacity = 2)
        {
            this.roomOwner = roomOwner;

            roomInfo = new RoomInfo();
            roomInfo.RoomName = roomName;
            roomInfo.RoomOwnerID = roomOwner.userInfo.AccountID;
            roomInfo.RoomCapacity = roomCapacity;
            roomInfo.UserCount = 1;
            roomInfo.ExistUserList.Add(roomOwner.userInfo);
            
            clientDict.Add(roomOwner.userInfo.AccountID, roomOwner);
            //userInfoDict.Add(roomOwner.userInfo.AccountID, roomOwner.userInfo);
        }
        public override string ToString()
        {
            return "roomOwner: " + roomOwner.userInfo.AccountID + " " + roomInfo.ToString();
        }

        /// <summary>
        /// 返回用户字典 用于为GamePack中对应的protobuf的map赋值
        /// https://learn.microsoft.com/zh-cn/dotnet/architecture/grpc-for-wcf-developers/protobuf-maps
        /// </summary>
        /// <returns></returns>
        //public IDictionary<string, UserInfo> GetUserDict()
        //{
        //    return userInfoDict;
        //}

        /// <summary>
        /// 设置GamePack包中的RoomInfo对象
        /// </summary>
        /// <param name="pack"></param>
        //public void SetRoomInfo(ref GamePack pack)
        //{
        //    //pack.RoomInfo.RoomID = roomInfo.RoomID;
        //    pack.RoomInfo.RoomName = roomInfo.RoomName;
        //    pack.RoomInfo.RoomOwnerID = roomOwner.userInfo.AccountID;
        //    pack.RoomInfo.RoomCapacity = roomInfo.RoomCapacity;
        //    pack.RoomInfo.UserCount = roomInfo.UserCount;
        //    //pack.RoomInfo.UserDict.Add(userInfoDict);
        //}

        /// <summary>
        /// 将玩家加入此房间
        /// </summary>
        public void JoinRoom(Client client)
        {
            //若玩家不在字典中，则加入字典
            if(clientDict.ContainsKey(client.userInfo.AccountID) == false)
            {
                clientDict.Add(client.userInfo.AccountID, client);
            }
            //if( ! userInfoDict.ContainsKey(client.userInfo.AccountID))
            //{
            //    userInfoDict.Add(client.userInfo.AccountID, client.userInfo);
            //}
            ++roomInfo.UserCount;

            GamePack pack = new GamePack();
            pack.RoomInfo = new RoomInfo();
            pack.UserInfo = new UserInfo();
           
            pack.MessageType = MessageType.Room;
            pack.ActionCode = ActionCode.ActionJoinRoom;
            pack.ReturnCode = ReturnCode.ReturnSucceed;

            foreach(var c in clientDict)
            {
                pack.RoomInfo.ExistUserList.Add(c.Value.userInfo);
            }

            //设置UserInfo
            pack.UserInfo = client.userInfo;

            //设置房间信息
            pack.RoomInfo.RoomOwnerID = roomOwner.userInfo.AccountID;
            pack.RoomInfo.RoomCapacity = roomInfo.RoomCapacity;
            pack.RoomInfo.UserCount = roomInfo.UserCount;
            pack.RoomInfo.RoomName = roomInfo.RoomName;
            //pack.RoomInfo.RoomID = roomData.RoomID;

            Console.WriteLine("JoinRoom broadcast: ");
            BroadcastMessageForAllClient(pack);
        }

        /// <summary>
        /// 玩家离开房间
        /// </summary>
        /// <param name="client"></param>
        public void LeaveRoom(Client client)
        {
            //如果没有离开者的信息，则返回
            if (clientDict.ContainsKey(client.userInfo.AccountID) == false) return;

            GamePack pack = new GamePack();
            //pack.RoomInfo = new RoomInfo();
            pack.UserInfo = new UserInfo();
            //设置离开者的ID
            pack.UserInfo.AccountID = client.userInfo.AccountID;
            pack.MessageType = MessageType.Room;
            pack.ActionCode = ActionCode.ActionLeaveRoom;

            --roomInfo.UserCount;
            //clientDict[client.userInfo.AccountID].Send(pack);

            //重置客户端玩家的准备和角色选择信息
            client.RestUserInfoState();
            //广播离开者信息
            Console.WriteLine("LeaveRoom broadcast: ");
            BroadcastMessageForAllClient(pack);

            //将离开者移除字典
            clientDict.Remove(client.userInfo.AccountID);
                                 
            //如果离开者是房主或者房间人数为0，清除房间
            if (roomInfo.UserCount <= 0 || roomOwner.userInfo.AccountID == client.userInfo.AccountID)
            {
                clientDict.Clear();
                RoomManager.Instance.RemoveRoom(this);              
            }
        }

        /// <summary>
        /// 更新准备就绪的玩家信息，由roomcontroller调用
        /// </summary>
        public void DealPreparedMessage(string accountID)
        {
            //如果房间存在当前accountID的玩家
            if(clientDict.ContainsKey(accountID))
            {
                //更新玩家准备状态
                clientDict[accountID].userInfo.IsPrepared = true;
                ++preparedUser;

                GamePack pack = new GamePack();
                pack.ActionCode = ActionCode.ActionPrepared;
                pack.ReturnCode = ReturnCode.ReturnSucceed;
                pack.MessageType = MessageType.Room;

                //设置准备的玩家ID
                pack.UserInfo = clientDict[accountID].userInfo;

                Console.WriteLine("DealPreparedMessage broadcast: ");
                BroadcastMessageForAllClient(pack);

                //所有玩家准备时
                if (isAllPlayerPrepared)
                {
                    //通知房间内所有成员准备加载游戏场景
                    Console.WriteLine("notice to start game");
                    ReadyToStartGame();
                }
            }
            //Console.WriteLine(userAccountID);
            //if (userInfoDict.ContainsKey(userAccountID) && choosedID != -1)
            //{
            //    //Console.WriteLine("contain");
            //    userInfoDict[userAccountID].IsPrepared = true;
            //    userInfoDict[userAccountID].CharacterChossedID = choosedID;
            //    ++roomInfo.PreparedUser;

            //    GamePack pack = new GamePack();
            //    pack.RoomInfo = new RoomInfo();
            //    pack.MessageType = MessageType.Room;
            //    pack.ActionCode = ActionCode.ActionPrepared;
            //    pack.RoomInfo.UserDict.Add(GetUserDict());

            //    BroadcastMessageForAllClient(pack);
            //}

            ////所有玩家准备时
            //if(roomInfo.isAllPlayerPrepared)
            //{
            //    //通知所有成员准备加载游戏场景
            //    Console.WriteLine("notice to start game");
            //    ReadyToStartGame();
            //}
        }

        public void DealPrepareCanceledMessage(string accountID)
        {
            //取得当前id在房间中的客户端
            if(clientDict.ContainsKey(accountID))
            {
                //设置准备状态为假
                clientDict[accountID].userInfo.IsPrepared = false;
                --preparedUser;

                GamePack pack = new GamePack();
                pack.ActionCode = ActionCode.ActionPreparedCancel;
                pack.ReturnCode = ReturnCode.ReturnSucceed;
                pack.MessageType = MessageType.Room;

                //设置取消准备的玩家信息
                pack.UserInfo = clientDict[accountID].userInfo;

                Console.WriteLine("DealPrepareCanceledMessage broadcast: ");
                BroadcastMessageForAllClient(pack);
            }

            //if(userInfoDict.ContainsKey(userAccountID))
            //{
            //    userInfoDict[userAccountID].IsPrepared = false;
            //    --roomInfo.PreparedUser;
            //    GamePack pack = new GamePack();
            //    pack.RoomInfo = new RoomInfo();
            //    pack.MessageType = MessageType.Room;
            //    pack.ActionCode = ActionCode.ActionPreparedCancel;
            //    pack.RoomInfo.UserDict.Add(GetUserDict());

            //    Console.WriteLine("isprepared : " + pack.RoomInfo.UserDict[userAccountID].IsPrepared);

            //    BroadcastMessageForAllClient(pack);
            //}
        }



        /// <summary>
        /// 通知房间所有客户端准备开始游戏
        /// </summary>
        private void ReadyToStartGame()
        {
            GamePack pack = new GamePack();
            pack.MessageType = MessageType.Room;
            pack.ActionCode = ActionCode.ActionReadyStartGame;
            pack.RoomInfo = new RoomInfo();
            pack.RoomInfo.ExistUserList.Add(GetRoomInfoExistUserList());

            BroadcastMessageForAllClient(pack);
        }

        private IList<UserInfo> GetRoomInfoExistUserList()
        {
            //为玩家赋予随机出生点
            List<int> position_list = Knuth_Durstenfeld_Shuffle();
            int i = 0;

            List<UserInfo> info_list = new List<UserInfo>();
            

            foreach (var client in clientDict)
            {
                client.Value.userInfo.CharacterSpawnPoint = position_list[i];
                ++i;
                info_list.Add(client.Value.userInfo);
            }

            return info_list;
        }

        /// <summary>/////////////////////////////////////////////////////
        /// 洗牌，为玩家们产生随机出生点
        /// </summary>
        /// <returns></returns>
        private List<int> Knuth_Durstenfeld_Shuffle()
        {
            List<int> position_list = new List<int>() { 0, 1, 2, 3, 4, 5 };
            Random rand = new Random((int)DateTime.Now.Ticks);
            
            for (int i = position_list.Count - 1; i >= 0; --i)
            {               
                SwapList(rand.Next() % (i + 1), i, ref position_list);
            }
            return position_list;
        }
        private void SwapList(int lhs, int rhs,ref List<int> list)
        {
            (list[rhs], list[lhs]) = (list[lhs], list[rhs]);
        }
        ////////////////////////////////////////////////////////////////


        /// <summary>
        /// 将包广播给房间中的所有客户端
        /// </summary>
        /// <param name="pack"></param>
        public void BroadcastMessageForAllClient(GamePack pack)
        {
            foreach(var client in clientDict)
            {
                //Console.WriteLine(pack != null);
                Console.WriteLine(client.Value.Socket.RemoteEndPoint + "  done");
                client.Value.Send(pack);
            }
        }

        public void BroadcastMessageForAllClient_UDP(GamePack pack)
        {
            foreach (var client in clientDict)
            {
                if (client.Value.RemoteIP == null) continue;
                //Console.WriteLine(pack != null);
                //Console.WriteLine("broadcast");
                
                ServerManager.Instance.Server_UDP.SendTo(pack, client.Value.RemoteIP);
            }
        }

        
    }
}
